<?php
/**
 * This file defines the {@see \BF\Validation\MailAddressValidator} class.
 *
 * @author     Kado <ulfikado@gmail.com>
 * @category   Beluga PHP5.3+ Framework
 * @package    BF\Validation
 * @since      2015-01-31 00:29
 * @subpackage Validators
 * @version    0.1
 */

namespace BF\Validation
{

   /**
    * A validator for testing a mail address.
    *
    * A Email address must have a <b>local-part</b> and a <b>domain-part</b> separated by an <b>@at</b> symbol.
    *
    *
    * <h3>The local part</h3>
    *
    * The local-part must be either a <b>dot-atom</b> or a <b>quoted string</b>,
    *
    * <h4>dot-atom</h4>
    *
    * A dot-atom may only contain letters, numbers, dots, and the following characters:
    *
    * <code>! # $ % & ‘ * + – / = ? ^ _ ` { | } ~</code>.
    *
    * However, neither the first nor the last character can be a dot, and two or more consecutive dots are not allowed.
    * The maximum length of a dot-atom is 64 characters.
    *
    * <h4>quoted string</h4>
    *
    * A quoted string may only contain printable US-ASCII characters or the space character, all contained within
    * double quotes.
    *
    * Double quotes and backslashes are allowed only if part of a quoted-pair (escaped with a backslash).
    * A quoted string may be empty. The maximum length of a quoted string is 64 characters, not including the
    * enclosing double-quotes or the escaping backslash of a quoted-pair.
    *
    *
    * <h3>The domain part</h3>
    *
    * The domain must be either a <b>domain name</b> or a <b>domain literal</b>.
    *
    * <h4>domain name</h4>
    *
    * A domain name consists of 1 to 127 labels (not including the (empty) root domain), separated by dots,
    * each containing any combination of letters, numbers, or hyphens. However, neither the first nor the last
    * character can be a hyphen. The maximum length of a domain name and label is 253 and 63 characters respectively.
    *
    * <h4>domain literal</h4>
    *
    * A domain literal is one of an <b>IPv4 address</b>, an <b>IPv6 address</b>, or an <b>IPv4-mapped IPv6 address</b>.
    *
    * When used as a domain literal in an email address, the IP address must be contained within square brackets [],
    * and IPv6 or IPv4-mapped IPv6 addresses must be preceded by a unquoted "IPv6:".
    *
    * <h5>IPv4 address</h5>
    *
    * An IPv4 address consists of four groups, separated by dots, each containing a decimal value between 0 and 255.
    *
    * <h5>IPv6 address</h5>
    *
    * An IPv6 address consists of eight groups, separated by colons, each containing a hexadecimal value between
    * 0 and FFFF.
    *
    * One or more consecutive groups of 0 value can be represented as a double colon; however, this may only occur once.
    *
    * <h5>IPv4-mapped IPv6 address</h5>
    *
    * An IPv4-mapped IPv6 address is an IPv6 address with the final two groups represented as an IPv4 address.
    *
    *
    * <h3>Comments + Folding Whitespaces</h3>
    *
    * Comments and folding white spaces are also allowed in an email address:
    *
    * - before and/or after the local-part
    * - before and/or after the domain
    * - before and/or after any dot in a local-part and/or domain
    *
    * Folding white space may also appear in a quoted string and/or in comments, and comments may nest.
    * A comment is almost identical to a quoted string except that it is opened and closed with a left and right
    * parentheses respectively and that parentheses are only allowed as part of a quoted-pair (or as further comments),
    * whereas double quotes may appear freely.
    *
    * Folding white spaces are occurrences of the space and/or horizontal tab character preceded by, optionally,
    * zero or more spaces and/or horizontal tabs followed by a carriage return and line feed pair.
    *
    * An obsolete form of folding white space is also allowed which is a carriage return and line feed pair followed
    * by a space or horizontal tab character.
    *
    * Folding white spaces, where allowed, are optional and may occur repeatedly.
    *
    * @property boolean $AllowQuotedString Is a quoted string local part allowed and a dot-atom not? (default=TRUE)
    * @property boolean $AllowObsolete Allow a obsolete version of the local-part. A mixture of atoms and quoted
    *           strings, separated by dots, is also allowed. An obsolete quoted string allows any US-ASCII character
    *           when part of a quoted-pair, and any US-ASCII character except the null, horizontal tab, new line,
    *           carriage return, backslash, and double quote characters when not. An obsolete local-part may only be
    *           empty if it is a single quoted string. The maximum length of an obsolete local-part, not including the
    *           double quotes enclosing a quoted string or the escaping backslash of a quoted-pair, is 64 characters.
    *           (default=FALSE)
    * @property boolean $RequireBasicDomainname A basic domain name is required? (dots are optional) (default=TRUE)
    * @property boolean $AllowIPAddresses A domain literal domain is allowed? It means IP-Adresses (default=FALSE)
    * @property boolean $AllowCommentsFoldingWhithespace Allow comments + folding whitespaces? (default=FALSE)
    * @property boolean $CheckForMX Check if a usable MX DNS entry exists by used mail address <b>domain-part</b>.
    *           If you enable this, remember this function does always DNS communication for getting the required
    *           Informations. This can slow down your application.
    * @property boolean $AllowLocal Allow known local domains or IPs as <b>domain-part</b>?
    *           e.g.: localhost, etc. (default=FALSE)
    * @property boolean $AllowReserved Allow known reserved hosts like example.* or *.test? (default=FALSE)
    * @property boolean $AllowGeographic Allow the use of known Geographic TLD's like 'berlin'? (default=TRUE)
    * @property boolean $AllowLocalized Allow the use of known localized unicode TLDs? (default=FALSE)
    * @property boolean $RequireTLD Is a tld definition required to be valid? (default=TRUE)
    * @property boolean $RequireKnownTLD The TLD must be defined and a known TLD? (default=TRUE)
    * @property boolean $AllowDynamic Allow mail addresses from dynamic dns services?
    * @property array   $TLDBlacklist Here you can define a blacklist of TLD's. Mailadresses using this TLDs will
    *           result in a validation error. (default=array())
    * @property array   $DomainBlacklist A list of not allowed mail adress domains. Mailadresses using this Domains
    *           will result in a validation error. (default=array())
    * @since    v0.1
    * @link     http://squiloople.com/2009/12/20/email-address-validation/#more-1 Thanks to michael from
    *           http://squiloople.com/ for the regex and some code and documentation!
    * @copyright © 2012 Michael Rushton
    * @copyright © 2014 Ulf Kadner
    * @author   Michael Rushton <michael@squiloople.com>
    * @author   Ulf Kadner <ulfikado@gmail.com>
    */
   class MailAddressValidator extends Validator
   {

      # <editor-fold defaultstate="collapsed" desc=" - - -   C O N S T A N T S   - - - - - - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * The RFC 5321 compatible check.
       */
      const RFC_5321 = 5321;

      /**
       * The RFC 5322 compatible check.
       */
      const RFC_5322 = 5322;

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - >   P U B L I C   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - -">

      /**
       * Inits a new instance.
       *
       * Usable options are:
       *
       * - <b>AllowQuotedString</b>: Is a quoted string local part allowed and a dot-atom not? (default=TRUE)
       * - <b>AllowObsolete</b>: Allow a obsolete version of the local-part. (default=FALSE)
       * - <b>RequireBasicDomainname</b>:  A basic domain name is required? (dots are optional) (default=TRUE)
       * - <b>AllowIPAddresses</b>: A domain literal domain is allowed? It means IP-Adresses (default=FALSE)
       * - <b>AllowCommentsFoldingWhithespace</b>: Allow comments + folding whitespaces? (default=FALSE)
       * - <b>CheckForMX</b>: Check if a usable MX DNS entry exists by used mail address <b>domain-part</b>.
       *   If you enable this, remember this function does always DNS communication for getting the required
       *   Informations. This can slow down your application.
       * - <b>AllowLocal</b>: Allow known local domains or IPs? e.g.: localhost, etc. (default=FALSE)
       * - <b>AllowReserved</b>: Allow known reserved hosts like example.* or *.test? (default=FALSE)
       * - <b>AllowGeographic</b>: Allow the use of known Geographic TLD's like 'berlin'? (default=TRUE)
       * - <b>AllowLocalized</b>: Allow the use of known localized unicode TLDs? (default=FALSE)
       * - <b>RequireTLD</b>: Is a tld definition required to be valid? (default=TRUE)
       * - <b>RequireKnownTLD</b>: The TLD must be defined and a known TLD?
       * - <b>AllowDynamic</b>: Allow mail addresses from dynamic dns services?
       * - <b>TLDBlacklist</b>: Here you can define a blacklist of TLD's.
       * - <b>DomainBlacklist</b>: A list of not allowed mail address domains.
       *
       * @param  array   $options  All validator options
       * @param  integer $standard The address standard to use (see self::RFC_532* class constants)
       * @throws \Exception        The exception is thrown if a unknown stantard is used.
       */
      public function __construct( array $options = array(), $standard = null )
      {

         self::setBooleanOption( 'AllowQuotedString',               $options, true );
         self::setBooleanOption( 'AllowObsolete',                   $options );
         self::setBooleanOption( 'RequireBasicDomainname',          $options, true );
         self::setBooleanOption( 'AllowIPAddresses',                $options );
         self::setBooleanOption( 'AllowCommentsFoldingWhithespace', $options );
         self::setBooleanOption( 'CheckForMX',                      $options );
         self::setBooleanOption( 'AllowLocal',                      $options );
         self::setBooleanOption( 'AllowReserved',                   $options );
         self::setBooleanOption( 'AllowGeographic',                 $options, true );
         self::setBooleanOption( 'AllowLocalized',                  $options );
         self::setBooleanOption( 'AllowWildcardTLD',                $options );
         self::setBooleanOption( 'RequireTLD',                      $options, true );
         self::setBooleanOption( 'RequireKnownTLD',                 $options, true );
         self::setBooleanOption( 'AllowDynamic',                    $options );
         self::setArrayOption  ( 'TLDBlacklist',                    $options );
         self::setArrayOption  ( 'DomainBlacklist',                 $options );
         self::setStringOption ( 'DisplayName',                     $options, \BF\_0( '_Validation', 'Mail address' ) );

         // Set the relevant standard or throw an exception if an unknown is requested
         switch ( $standard )
         {

            case null:
               // Do nothing if no standard requested
               break;

            case self::RFC_5321:
               // Otherwise if RFC 5321 requested
               $this->setStandard5321();
               break;

           // Otherwise if RFC 5322 requested
           case self::RFC_5322:
             $this->setStandard5322();
             break;

           // Otherwise throw an exception
           default:
             throw new \Exception('Unknown RFC standard for email address validation.');
         }

         parent::__construct( $options );

      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - - -">

      /**
       * Validate the email address using a basic standard.
       *
       * @return \BF\Validation\MailAddressValidator
       */
      public function setStandardBasic()
      {

         // A quoted string local part is not allowed
         $this->_options[ 'AllowQuotedString' ] = false;

         // An obsolete local part is not allowed
         $this->_options[ 'AllowObsolete' ] = false;

         // A basic domain name is required
         $this->_options[ 'RequireBasicDomainname' ] = true;

         // A domain literal domain is not allowed
         $this->_options[ 'AllowIPAddresses' ] = false;

         // Comments and folding white spaces are not allowed
         $this->_options[ 'AllowCommentsFoldingWhithespace' ] = false;

         $this->LastResult = null;

         // Return the \BF\Validation\MailAddressValidator object
         return $this;

      }

      /**
       * Validate the email address using RFC 5321.
       *
       * @return \BF\Validation\MailAddressValidator
       */
      public function setStandard5321()
      {

         // A quoted string local part is allowed
         $this->_options[ 'AllowQuotedString' ] = true;

         // An obsolete local part is not allowed
         $this->_options[ 'AllowObsolete' ] = false;

         // Only a basic domain name is not required
         $this->_options[ 'RequireBasicDomainname' ] = false;

         // A domain literal domain is allowed
         $this->_options[ 'AllowIPAddresses' ] = true;

         // Comments and folding white spaces are not allowed
         $this->_options[ 'AllowCommentsFoldingWhithespace' ] = false;

         $this->LastResult = null;

         // Return the \BF\Validation\MailAddressValidator object
         return $this;

      }

      /**
       * Validate the email address using RFC 5322
       *
       * @return \BF\Validation\MailAddressValidator
       */
      public function setStandard5322()
      {

         // A quoted string local part is disallowed
         $this->_options[ 'AllowQuotedString' ] = false;

         // An obsolete local part is allowed
         $this->_options[ 'AllowObsolete' ] = true;

         // Only a basic domain name is not required
         $this->_options[ 'RequireBasicDomainname' ] = false;

         // A domain literal domain is allowed
         $this->_options[ 'AllowIPAddresses' ] = true;

         // Comments and folding white spaces are allowed
         $this->_options[ 'AllowCommentsFoldingWhithespace' ] = true;

         $this->LastResult = null;

         // Return the \BF\Validation\MailAddressValidator object
         return $this;

      }

      /**
       * Sets a value of a option.
       *
       * @param string $optionName The name of the option
       * @param mixed  $value THe value to set. (The type is depending to the option it self)
       */
      public function setOption( $optionName, $value )
      {
         $tmp = null;
         switch ( $optionName )
         {
            case 'AllowQuotedString':
            case 'AllowObsolete':
            case 'AllowCommentsFoldingWhithespace':
            case 'CheckForMX':
            case 'RequireTLD':
            case 'RequireKnownTLD':
            case 'AllowDynamic':
               if ( \BF\TypeTool::IsBoolConvertible( $value, $tmp ) )
               {
                  $this->_options[ $optionName ] = $tmp;
               }
               elseif ( \is_null( $value ) )
               {
                  $this->_options[ $optionName ] = false;
               }
               $this->LastResult = null;
               break;
            case 'RequireBasicDomainname':
            case 'AllowIPAddresses':
            case 'AllowLocal':
            case 'AllowReserved':
            case 'AllowGeographic':
            case 'AllowLocalized':
               if ( \BF\TypeTool::IsBoolConvertible( $value, $tmp ) )
               {
                  $this->_options[ $optionName ] = $tmp;
               }
               elseif ( \is_null( $value ) )
               {
                  $this->_options[ $optionName ] = true;
               }
               $this->LastResult = null;
               break;
            case 'TLDBlacklist':
            case 'DomainBlacklist':
               if ( \is_array( $value ) )
               {
                  $this->_options[ $optionName ] = $value;
               }
               elseif ( \is_null( $value ) )
               {
                  $this->_options[ $optionName ] = array();
               }
               $this->LastResult = null;
               break;
            default:
               parent::setOption( $optionName, $value );
               break;
         }
      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R I V A T E   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

      /**
       * Returns the regular expression element, for a dot atom local part.
       *
       * @return string
       */
      private function getDotAtomRegexElement()
      {
         return "([!#-'*+\/-9=?^-~-]+)(?>\.(?1))*";
      }

      /**
       * Returns the regular expression element of a quoted string local part
       *
       * @return string
       */
      private function getQuotedStringRegexElement()
      {
        return '"(?>[ !#-\[\]-~]|\\\[ -~])*"';
      }

      /**
       * Returns the regular expression element for an obsolete local part
       *
       * @return string
       */
      private function getObsoleteRegexElementRegexElement()
      {

         return '([!#-\'*+\/-9=?^-~-]+|"(?>'
            . $this->getFWSRegexElement()
            . '(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*'
            . $this->getFWSRegexElement()
            . '")(?>'
            . $this->getCFWSRegexElement()
            . '\.'
            . $this->getCFWSRegexElement()
            . '(?1))*';

      }

      /**
       * Returns the regular expression element for a domain name domain
       *
       * @return string
       */
      private function getDomainNameRegexElement()
      {

         // Return the basic domain name format if required
         if ( $this->_options[ 'RequireBasicDomainname' ] )
         {

           return '(?>' . $this->getDomainNameLengthLimitRegexElement()
             . '[a-z\d](?>[a-z\d-]*[a-z\d])?'
             . $this->getCFWSRegexElement()
             . '\.'
             . $this->getCFWSRegexElement()
             . '){1,126}[a-z]{2,6}';

         }

         // Otherwise return the full domain name format
         return $this->getDomainNameLengthLimitRegexElement()
           . '([a-z\d](?>[a-z\d-]*[a-z\d])?)(?>'
           . $this->getCFWSRegexElement()
           . '\.'
           . $this->getDomainNameLengthLimitRegexElement()
           . $this->getCFWSRegexElement()
           . '(?2)){0,126}';

      }

      /**
       * Returns the regular expression element of an IPv6 address
       *
       * @return string
       */
      private function getIPv6RegexElement()
      {
         return '([a-f\d]{1,4})(?>:(?3)){7}|(?!(?:.*[a-f\d][:\]]){8,})((?3)(?>:(?3)){0,6})?::(?4)?';
      }

      /**
       * Returns the regular expression element for an IPv4-mapped IPv6 address
       *
       * @return string
       */
      private function getIPv4MappedIPv6RegexElement()
      {
         return '(?3)(?>:(?3)){5}:|(?!(?:.*[a-f\d]:){6,})(?5)?::(?>((?3)(?>:(?3)){0,4}):)?';
      }

      /**
       * Returns the regular expression element of an IPv4 address
       *
       * @return string
       */
      private function getIPv4RegexElement()
      {
         return '(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(?>\.(?6)){3}';
      }

      /**
       * Returns the regular expression element for a domain literal domain
       *
       * @return string
       */
      private function getDomainLiteralRegexElement()
      {

         return '\[(?:(?>IPv6:(?>'
            . $this->getIPv6RegexElement()
            . '))|(?>(?>IPv6:(?>'
            . $this->getIPv4MappedIPv6RegexElement()
            . '))?'
            . $this->getIPv4RegexElement()
            . '))\]';

      }

      /**
       * Returns either the regular expression element for folding white spaces or its backreference.
       *
       * @param  boolean $define
       * @return string
       */
      private function getFWSRegexElement( $define = false )
      {

         // Return the backreference if $define is set to FALSE otherwise return the regular expression
         if ( $this->_options[ 'AllowCommentsFoldingWhithespace' ] )
         {
            return ! $define ? '(?P>fws)' : '(?<fws>(?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)';
         }

         return false;

      }

      /**
       * Returns the regular expression element for comments
       *
       * @return string
       */
      private function getCommentsRegexElement()
      {

         return '(?<comment>\((?>'
            . $this->getFWSRegexElement()
            . '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?P>comment)))*'
            . $this->getFWSRegexElement()
            . '\))';

      }

      /**
       * Returns either the regular expression element for comments and folding white spaces or its backreference
       *
       * @param  boolean $define
       * @return string
       */
      private function getCFWSRegexElement( $define = false )
      {

         // Return the backreference if $define is set to FALSE
         if ( $this->_options[ 'AllowCommentsFoldingWhithespace' ] && ! $define )
         {
            return '(?P>cfws)';
         }

         // Otherwise return the regular expression
         if ( $this->_options[ 'AllowCommentsFoldingWhithespace' ] )
         {

            return '(?<cfws>(?>(?>(?>'
               . $this->getFWSRegexElement( true )
               . $this->getCommentsRegexElement()
               . ')+'
               . $this->getFWSRegexElement()
               . ')|'
               . $this->getFWSRegexElement()
               . ')?)';

         }

         return '';

      }

      /**
       * Establish and return the valid format for the local part
       *
       * @return string
       */
      private function getLocalPartRegexElement()
      {

         // The local part may be obsolete if allowed
         if ( $this->_options[ 'AllowObsolete' ] )
         {
            return $this->getObsoleteRegexElementRegexElement();
         }

         // Otherwise the local part must be either a dot atom or a quoted string if the latter is allowed
         if ( $this->_options[ 'AllowQuotedString' ] )
         {
            return '(?>' . $this->getDotAtomRegexElement() . '|' . $this->getQuotedStringRegexElement() . ')';
         }

         // Otherwise the local part must be a dot atom
         return $this->getDotAtomRegexElement();

      }

      /**
       * Establish and return the valid format for the domain
       *
       * @return string
       */
      private function getDomainRegexElement()
      {

        // The domain must be either a domain name or a domain literal if the latter is allowed
        if ( $this->_options[ 'AllowDomainLiteral' ] )
        {
          return '(?>' . $this->getDomainNameRegexElement() . '|' . $this->getDomainLiteralRegexElement() . ')';
        }

        // Otherwise the domain must be a domain name
        return $this->getDomainNameRegexElement();

      }

      /**
       * Return the email address length limit
       *
       * @return string
       */
      private function getEmailAddressLengthLimit()
      {
         return '(?!(?>' . $this->getCFWSRegexElement() . '"?(?>\\\[ -~]|[^"])"?' . $this->getCFWSRegexElement() . '){255,})';
      }

      /**
       * Return the local part length limit
       *
       * @return string
       */
      private function getLocalPartLengthLimit()
      {
         return '(?!(?>' . $this->getCFWSRegexElement() . '"?(?>\\\[ -~]|[^"])"?' . $this->getCFWSRegexElement() . '){65,}@)';
      }

      /**
       * Establish and return the domain name length limit
       *
       * @return string
       */
      private function getDomainNameLengthLimitRegexElement()
      {
         return '(?!' . $this->getCFWSRegexElement() . '[a-z\d-]{64,})';
      }

      /**
       * Check to see if the domain can be resolved to MX RRs
       *
       * @param  array $domain
       * @return integer|boolean
       */
      private function verifyDomain( $domain )
      {

        return \checkdnsrr( \end( $domain ), 'MX' );

      }

      # </editor-fold>

      # <editor-fold defaultstate="collapsed" desc=" - - -   P R O T E C T E D   M E T H O D S   - - - - - - - - - - - - - - - - - - -">

      /**
       * @return boolean
       */
      protected function __validate()
      {

         // Build the required regular expression
         // Thanks to http://squiloople.com/2009/12/20/email-address-validation/#more-1
         $regex =
              '/^'
            . $this->getEmailAddressLengthLimit()
            . $this->getLocalPartLengthLimit()
            . $this->getCFWSRegexElement()
            . $this->getLocalPartRegexElement()
            . $this->getCFWSRegexElement()
            . '@'
            . $this->getCFWSRegexElement()
            . $this->getDomainRegexElement()
            . $this->getCFWSRegexElement( true )
            . '$/isD';

         $this->_message = '';

         if ( ! \preg_match( $regex, $this->_value ) )
         {

            $this->_message = \BF\_0(
               '_Validation',
               'This is not a valid mail address!'
            );

            return $this->__setLastResult( false );

         }

         list( , $domainPart ) = explode( '@', $this->_value, 2 );

         if ( false === ( $domain = \BF\Net\Domain::Parse( $domainPart, $this->_options[ 'RequireKnownTLD' ] ) ) )
         {

            $this->_message = \BF\_0(
               '_Validation',
               'The mail address does not contain a valid domain part!'
            );

            return $this->__setLastResult( false );

         }

         if ( ! $this->_options[ 'AllowLocal' ] && $domain->IsLocal )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept any locally reserved mail address domain-parts!'
            );
            return $this->__setLastResult( false );
         }

         if ( ! $this->_options[ 'AllowReserved' ] && $domain->IsReserved )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept any reserved mail address domain-parts!'
            );
            return $this->__setLastResult( false );
         }

         if ( ! $this->_options[ 'AllowGeographic' ] && $domain->IsGeographic )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept mail addresses, pointing to geoagraphic TLDs!'
            );
            return $this->__setLastResult( false );
         }

         if ( ! $this->_options[ 'AllowLocalized' ] && $domain->IsLocalized )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept some localized unicode mail address domain parts!'
            );
            return $this->__setLastResult( false );
         }

         if ( $this->_options[ 'RequireTLD' ] && ! $domain->HasTLD )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept domain parts without a TLD definition!'
            );
            return $this->__setLastResult( false );
         }

         if ( $this->_options[ 'RequireKnownTLD' ] && ! $domain->HasKnownTLD )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept domain parts without a definition of a known TLD!'
            );
            return $this->__setLastResult( false );
         }

         if ( ! $this->_options[ 'AllowDynamic' ] && $domain->IsDynamic )
         {
            $this->_message = \BF\_0(
               '_Validation',
               'We do not accept domain parts pointing to a known dynamic DNS service!'
            );
            return $this->__setLastResult( false );
         }

         if ( $domain->HasTLD && \count( $this->_options[ 'TLDBlacklist' ] ) > 0 )
         {
            if ( \in_array( $domain->SLD->TLD->toString(), $this->_options[ 'TLDBlacklist' ] ) )
            {
               $this->_message = \BF\_0(
                  '_Validation',
                  'We do not accept the defined mail address (forbidden TLD part)!'
               );
               return $this->__setLastResult( false );
            }
         }

         if ( \count( $this->_options[ 'DomainBlacklist' ] ) > 0 )
         {
            if ( \in_array( $domain->SLD->toString(), $this->_options[ 'DomainBlacklist' ] )
              || \in_array( $domain->toString(), $this->_options[ 'DomainBlacklist' ] ))
            {
               $this->_message = \BF\_0(
                  '_Validation',
                  'We do not accept the defined mail address (forbidden domain part)!'
               );
               return $this->__setLastResult( false );
            }
         }

         if ( $this->_options[ 'CheckForMX' ] )
         {
            if ( ! $this->verifyDomain( $domainPart ) )
            {
               $this->_message = \BF\_0(
                  '_Validation',
                  'This mail address uses a invalid domain-part! (No MX host)'
               );
               return $this->__setLastResult( false );
            }
         }

         return $this->__setLastResult( true );

      }

      # </editor-fold>

   }

}

